<?php
/**
 * XML元素分析器
 *
 * @version $Id: IElementParser.php 323 2008-05-14 05:49:20Z iwind $
 * @package util.sqlmap2
 */
class IElementParser extends IObject {
	private $connectionSetting = array();
	private $elementMap = array();
	private $resourceFiles = array();
	
	/**
	 * 构造器
	 * 
	 * 从0.2.1开始可以传入一个数组文件
	 *
	 * @param IFile $configFile 配置文件对象，格式为XML或array
	 */
	public function __construct(IFile $configFile) {
		$dirname = $configFile->getParent();
		$isArrayFormat = false;
		if ($configFile->getExtension() == "php") {
			$isArrayFormat = true;
			$setting = require($configFile->getAbsolutePath());
		}
		
		//是否有缓存
		if (IAPC::isEnabled()) {
			$apcCacheKey = "file://" . $configFile->getAbsolutePath();
			$apcEntry = IObjectFactory::getSingleObject("IApcCacheFactory")->getEntry($apcCacheKey);
			if ($apcEntry != null) {
				$apcLastModified = $apcEntry->getLastModified();
				$apcCacheArray = $apcEntry->getValue();
				if ($apcLastModified > $configFile->getLastModified() && !$this->resourceIsModified($apcCacheArray["resourceFiles"], $apcLastModified)) {
					$this->connectionSetting = $apcCacheArray["connectionSetting"];
					$this->elementMap = $apcCacheArray["elementMap"];
					//ILogger::debug("load from apc cache");
					return;
				}
			}
		}
		
		if (!$isArrayFormat) {
			$xml = $configFile->getReader()->read();
			
			$dom = new DOMDocument();
			$dom->loadXML($xml);
			
			$xpath = new DOMXPath($dom);
			
			//connection setting
			$nodeList = $xpath->query("/sqlMapConfig/connection");
			if ($nodeList->length > 0) {
				$node = $nodeList->item(0);
				$childNodes = $node->childNodes;
				foreach ($childNodes as $childNode) {
					if ($childNode->nodeType == XML_ELEMENT_NODE) {
						$this->connectionSetting[$childNode->localName] = $childNode->textContent;
					}
				}
			}
			
			//sql elements
			$nodeList = $xpath->query("/sqlMapConfig/sqlMap[@resource]");
			if ($nodeList->length > 0) {
				foreach ($nodeList as $resourceNode) {
					$this->parseResource($dirname, $resourceNode->attributes->getNamedItem("resource")->textContent);
				}
			}
		}
		else {
			$this->connectionSetting = $setting["connection"];
			if (isset($setting["resources"]) && is_array($setting["resources"])) {
				foreach ($setting["resources"] as $resource) {
					$this->parseResource($dirname, $resource);
				}
			}
		}
		
		//缓存
		if (IAPC::isEnabled()) {
			$apcEntry = new ICacheEntryApdater();
			$apcEntry->setKey($apcCacheKey);
			$apcEntry->setTtl(3600);
			$apcEntry->setValue(array("connectionSetting"=>$this->connectionSetting, "elementMap"=>$this->elementMap, "resourceFiles"=>$this->resourceFiles));
			IObjectFactory::getSingleObject("IApcCacheFactory")->putEntry($apcEntry);
		}
	}
	
	/**
	 * 分析sqlmap resource
	 *
	 * @since 0.2.1
	 */
	private function parseResource($dirname, $resourceXmlFilePath) {
			$resourceXmlFile = new IFile($dirname . "/" . $resourceXmlFilePath);
			if (!$resourceXmlFile->exists()) {
				ILogger::warning("can't find sqlmap resource xml file '" . $resourceXmlFile->getPath() . " '");
			}
			else {
				//record resource file
				$this->resourceFiles[] = $resourceXmlFile->getAbsolutePath();
				
				//start to parse resource file
				$resourceDocument = new DOMDocument();
				$resourceDocument->load($resourceXmlFile->getABsolutePath());
				$resourceNode = new IXmlNode();
				$resourceNode->loadFromDOM($resourceDocument);
				
				//sql maps
				$sqlMapNodeList = $resourceNode->getNamedChildNodes("sqlMap");
				if (!empty($sqlMapNodeList)) {
					$namespace = "";
					$sqlMapNode = $sqlMapNodeList[0];
					
					//namespace
					$namespace = trim($sqlMapNode->getAttribute("namespace"));
											
					//child nodes
					$queryNodeList = $sqlMapNode->getChildNodes();
					foreach ($queryNodeList as $queryNode) {
						if ($queryNode->getType() == XML_ELEMENT_NODE) {
							$tagName = strToLower($queryNode->getName());
							$queryId = trim($queryNode->getAttribute("id"));
							if (isset(ISqlMapConfig::$elements[$tagName]) && $queryId != "") {
								$elementClass = ISqlMapConfig::$elements[$tagName];
								$element = new $elementClass();
								$element->setNode($queryNode);
								$element->setElementMap($this->elementMap);
								if ($namespace != "") {
									$queryId = $namespace . "." . $queryId;
									$element->setNamespace($namespace);
								}
								$this->elementMap[$queryId] = $element;
							}
						}
					}
				}
			}
	}
	
	private function resourceIsModified(array $resourceFiles, $time) {
		foreach ($resourceFiles as $resourceFile) {
			if (!is_file($resourceFile)) {
				return true;
			}
			if (filemtime($resourceFile) >= $time) {
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 取得数据库连接设置.
	 *
	 * @return array
	 */
	public function getConnectionSetting() {
		return $this->connectionSetting;
	}
	
	/**
	 * 取得数据库连接属性
	 *
	 * @param string $key 键
	 * @return string
	 * @since 0.1
	 */
	public function getConnectionProperty($key) {
		if (array_key_exists($key, $this->connectionSetting)) {
			return $this->connectionSetting[$key];
		}
		return null;
	}
	
	/**
	 * 根据sqlId取得对应的XML元素
	 *
	 * @param string $sqlId SQL ID
	 * @return IElement/null
	 */
	public function getElement($sqlId) {
		if (isset($this->elementMap[$sqlId])) {
			return $this->elementMap[$sqlId];
		}
		return null;
	}
	
	/**
	 * 取得元素表(element map)
	 *
	 * @return array
	 */
	public function getElementMap() {
		return $this->elementMap;
	}
}

?>